#include "preforkserver.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <fcntl.h>  

#define MAXBUF 1024

const int SHMKEY = 1234555;
const int SUBPROCNUMB = 20;

int SetNonBlock(int sockfd)  
{  
	if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK) == -1)  
	{  
		return -1;  
	}  
	return 0;  
}

class PreForkServerImpl : public PreForkServer
{
public:
	PreForkServerImpl(int key, unsigned int aiSubprocnum):PreForkServer(key,aiSubprocnum){};
protected:
	virtual void ChildRun();
	virtual void BeforeChildRun();
	virtual void BeforeRun();
private:
	int listenfd;	
};

void PreForkServerImpl :: BeforeRun()
{
	printf("pid[%d] before run\n",getpid());

	struct sockaddr_in svraddr;
	listenfd = socket( AF_INET, SOCK_STREAM, 0 );
	SetNonBlock( listenfd );
	bzero( &svraddr, sizeof(sockaddr_in) );
	svraddr.sin_family = AF_INET;
	inet_aton( "127.0.0.1", &(svraddr.sin_addr) );
	svraddr.sin_port = htons( 8001 );
	bind( listenfd, (sockaddr *)&svraddr, sizeof(svraddr) );
	listen(listenfd,10);
}


void PreForkServerImpl :: ChildRun()
{
	socklen_t clilen;
	int nfds,connfd,n,sockfd,epfd;
	struct epoll_event ev,events[20];
	struct sockaddr_in cliaddr;
	
	epfd = epoll_create(1024);
	ev.data.fd = listenfd;
	ev.events = EPOLLIN|EPOLLET;
	epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);
	
	for(;;)
	{
		nfds = epoll_wait( epfd, events, 20, 500);

		for (int i=0; i<nfds; ++i)
		{
			if ( events[i].data.fd == listenfd )
			{
				connfd = accept( listenfd, (sockaddr *)&cliaddr, &clilen );
				if ( connfd < 0 )
				{
					perror( "error" );
					exit(0);
				}
				SetNonBlock(connfd);
				printf("pid[%d] accept a client, use fd[%d]\n",getpid(),connfd);
				ev.data.fd = connfd;
				ev.events = EPOLLIN|EPOLLET;

				epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);
			}
			else if (events[i].events & EPOLLIN )
			{
				char * line = new char[512];
				if ( (sockfd = events[i].data.fd) < 0) continue;
				
				n = read(sockfd, line, 512 );
				if ( n <= 0)
			   	{
					close(sockfd);
					ev.data.fd = sockfd;
					ev.events = EPOLLOUT|EPOLLET;
					epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,&ev);
					
					events[i].data.fd = -1;
					printf("pid[%d] read line'length[%d] server close fd[%d]\n",getpid(),n,sockfd);
					if ( n<0 )
					{
						printf("errno %d errmsg %s\n", errno, strerror(errno) ); 
					}
				} 
				else //n>0
				{
					printf("pid[%d] client say: %s\n",getpid(),line);
					printf("line address %d\n",line);
					//bzero( line, sizeof(line) );dd
					//设置用于写操作的文件描述符
					ev.data.ptr=line;
					ev.data.fd=sockfd;
					//设置用于注测的写操作事件
					ev.events=EPOLLOUT|EPOLLET;
					//修改sockfd上要处理的事件为EPOLLOUT
					epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
				}
			}
			else if(events[i].events&EPOLLOUT)
			{
				printf("pid[%d] write back\n", getpid());
				char * lineReturn = (char *) events[i].data.ptr;
				sockfd = events[i].data.fd;
				printf("line address %d\n",lineReturn);
				int flag  = write(sockfd, lineReturn, 10 );
				if ( flag < 0 )
				{
					printf("flag %d fd %d errno %d errmsg %s \n",flag,sockfd,errno,strerror(errno));
				}

				//设置用于读操作的文件描述符
				ev.data.fd=sockfd;
				//设置用于注测的读操作事件
				ev.events=EPOLLIN|EPOLLET;
				//修改sockfd上要处理的事件为EPOLIN
				epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
			}
		}
	}
}

void PreForkServerImpl :: BeforeChildRun()
{
}

int main(int argc, char* argv[])
{ 
	int ret = -1;
	PreForkServerImpl server(SHMKEY, SUBPROCNUMB);
	ret = server.Init();

	if (ret == 0)
	{
		server.Run();
	}
	else
	{
		printf("Server Init fail, ret %d\n",ret);
	}

	return 0;
}
